﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using EpiNEXT.Core.Fields;

namespace EpiNEXT.WPF
{
    /// <summary>
    /// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
    ///
    /// Step 1a) Using this custom control in a XAML file that exists in the current project.
    /// Add this XmlNamespace attribute to the root element of the markup file where it is 
    /// to be used:
    ///
    ///     xmlns:MyNamespace="clr-namespace:EpiNEXT.WPF"
    ///
    ///
    /// Step 1b) Using this custom control in a XAML file that exists in a different project.
    /// Add this XmlNamespace attribute to the root element of the markup file where it is 
    /// to be used:
    ///
    ///     xmlns:MyNamespace="clr-namespace:EpiNEXT.WPF;assembly=EpiNEXT.WPF"
    ///
    /// You will also need to add a project reference from the project where the XAML file lives
    /// to this project and Rebuild to avoid compilation errors:
    ///
    ///     Right click on the target project in the Solution Explorer and
    ///     "Add Reference"->"Projects"->[Browse to and select this project]
    ///
    ///
    /// Step 2)
    /// Go ahead and use your control in the XAML file.
    ///
    ///     <MyNamespace:DataEntryCanvas/>
    ///
    /// </summary>
    public class DataEntryCanvas : ItemsControl, IDisposable //Canvas
    {
        static DataEntryCanvas()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(DataEntryCanvas), new FrameworkPropertyMetadata(typeof(DataEntryCanvas)));
        }

        private Dictionary<UIElement, IField> Controls { get; set; }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                foreach (KeyValuePair<UIElement, IField> kvp in Controls)
                {
                    kvp.Key.GotFocus -= Key_GotFocus;
                    kvp.Key.LostFocus -= Key_LostFocus;
                }

                var canvas = this.GetTemplateChild("canvasMain") as Canvas;
                if (canvas != null)
                {
                    foreach (UIElement element in canvas.Children)
                    {
                        canvas.Children.Remove(element);
                    }
                    canvas.Children.Clear();
                    canvas = null;
                }

                this.IsVisibleChanged -= DataEntryCanvas_IsVisibleChanged;
            }
            // free native resources if there are any.
        }

        public override void OnApplyTemplate()
        {
            this.IsVisibleChanged += DataEntryCanvas_IsVisibleChanged;
            base.OnApplyTemplate();

            if (this.DataContext is PageViewModel) 
            { 
                PageViewModel pageVM = (this.DataContext as PageViewModel);

                ProjectViewModel projectVM = pageVM.FormVM.ProjectVM;
                this.DataContext = projectVM;

                var canvas = this.GetTemplateChild("canvasMain") as Canvas;
                if (canvas != null)
                {
                    ((Canvas)canvas).Margin = new Thickness(35, 35, 35, 35);

                    var dc = (this.GetTemplateChild("borderMain") as Border).DataContext;
                    ControlFactory factory = new ControlFactory(BindingMode.TwoWay);

                    Controls = factory.CreateControlsForPage(pageVM);

                    foreach (KeyValuePair<UIElement, IField> kvp in this.Controls)
                    {
                        if (kvp.Value.PageId.Equals(pageVM.Page.PageId))
                        {
                            ((Canvas)canvas).Children.Add(kvp.Key);

                            //if (kvp.Value is DataField && kvp.Value is IInputField && !(kvp.Key is Label))
                            //{
                                /* NOTE: Showing tabs #'s as an adorner layer interferes with data entry validation 
                                 * checking, so only use it for form design mode. There's probably a way to solve
                                 * this so both can exist but showing tabs in Enter is not that critical, so I'm
                                 * not going to spend the time to investigate it and fix it.
                                 */

                                //AdornerLayer layer = AdornerLayer.GetAdornerLayer(kvp.Key);

                                //TextBlock info = new TextBlock();
                                //info.Padding = new Thickness(4);
                                //info.Background = Brushes.Black;
                                //info.Foreground = Brushes.White;
                                //info.Text = kvp.Value.TabIndex.ToString();

                                //Binding visBinding = new Binding("IsShowingTabIndices");
                                //visBinding.Mode = BindingMode.OneWay;
                                //visBinding.Converter = this.TryFindResource("BooleanToVisibilityConverter") as BooleanToVisibilityConverter;

                                //layer.SetBinding(AdornerLayer.VisibilityProperty, visBinding);

                                //layer.Add(new Adorners.TextBlockAdorner(kvp.Key, info));
                            //}

                            // I don't like wiring up events like this; need to look into replacing 
                            // control factory pattern and event handling with something better suited
                            // to MVVM.
                            kvp.Key.GotFocus += Key_GotFocus;
                            kvp.Key.LostFocus += Key_LostFocus;
                        }
                    }
                }
            }
            else
            {

            }
        }

        void DataEntryCanvas_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            // This is ugly but it works.

            /* This fixes a performance problem. The dynamic objects (Record.cs) may only have a few fields
             * on, say, a child form... however, the way these templates are set up, the controls bound to the
             * parent form are still active and therefore cause a TryGetMember call on each Record. This means
             * navigating through records slows down tremendously since any properties that aren't found cause
             * enormous performance hits. This code makes sure we clear the parent form when navigating to a
             * child so that we're not trying to bind controls that don't appear (and shouldn't even exist, really)
             * to records for a different form.
             */

            var canvas = this.GetTemplateChild("canvasMain") as Canvas;
            if (canvas != null)
            {
                if (Controls != null)
                {
                    foreach (KeyValuePair<UIElement, IField> kvp in this.Controls)
                    {
                        kvp.Key.GotFocus -= Key_GotFocus;
                        kvp.Key.LostFocus -= Key_LostFocus;

                        ((Canvas)canvas).Children.Remove(kvp.Key);
                    }
                }

                canvas = null;
            }

            this.Dispose();
            GC.Collect();
        }

        private async void Key_LostFocus(object sender, RoutedEventArgs e)
        {
            if (this.DataContext is ProjectViewModel)
            {
                ProjectViewModel vm = this.DataContext as ProjectViewModel;
                await Task.Factory.StartNew(delegate
                {
                    vm.CurrentField = String.Empty;
                });
            }
        }

        private async void Key_GotFocus(object sender, RoutedEventArgs e)
        {
            if (this.DataContext is ProjectViewModel)
            {
                ProjectViewModel vm = this.DataContext as ProjectViewModel;

                await Task.Factory.StartNew(delegate
                {
                    IField field = this.Controls[sender as UIElement];

                    foreach (KeyValuePair<UIElement, IField> kvp in this.Controls)
                    {
                        if (kvp.Key == sender)
                        {
                            string currentField = field.Name + " [" + field.FieldType + "]";
                            vm.CurrentField = currentField;
                            break;
                        }
                    }
                });
            }
        }

        public static readonly DependencyProperty PageIdProperty = DependencyProperty.Register("PageId", typeof(Guid), typeof(DataEntryCanvas));
        public Guid PageId
        {
            get
            {
                return (Guid)(this.GetValue(PageIdProperty));
            }
            set
            {
                this.SetValue(PageIdProperty, value);
            }
        }
    }
}
